﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using VisualView.Axis;

namespace VisualView.Series
{
    /// <summary>
    /// XY图表基类
    /// </summary>
    public abstract class BaseXYSeries<TX, TY> : GeometryView
    {
        /// <summary>
        /// X轴
        /// </summary>
        public BaseAxis<TX> XAxis
        {
            get { return (BaseAxis<TX>)GetValue(XAxisProperty); }
            set { SetValue(XAxisProperty, value); }
        }
        public static readonly DependencyProperty XAxisProperty = DependencyProperty.Register("XAxis", typeof(BaseAxis<TX>), typeof(BaseXYSeries<TX, TY>), new PropertyMetadata((d, e) =>
        {
            if (d is BaseXYSeries<TX, TY> chart)
            {
                if (e.OldValue != null && e.OldValue is BaseAxis<TX> oldaxis) oldaxis.RangeChangedEvent -= chart.XAxisRangeChangedNeedRedraw;
                if (e.NewValue != null && e.NewValue is BaseAxis<TX> newaxis) newaxis.RangeChangedEvent += chart.XAxisRangeChangedNeedRedraw;
                chart.Draw();
            }
        }));
        protected virtual void XAxisRangeChangedNeedRedraw(BaseAxis<TX> axis) { Draw(); }

        /// <summary>
        /// Y轴
        /// </summary>
        public BaseAxis<TY> YAxis
        {
            get { return (BaseAxis<TY>)GetValue(YAxisProperty); }
            set { SetValue(YAxisProperty, value); }
        }
        public static readonly DependencyProperty YAxisProperty = DependencyProperty.Register("YAxis", typeof(BaseAxis<TY>), typeof(BaseXYSeries<TX, TY>), new PropertyMetadata((d, e) =>
        {
            if (d is BaseXYSeries<TX, TY> chart)
            {
                if (e.OldValue != null && e.OldValue is BaseAxis<TY> oldaxis) oldaxis.RangeChangedEvent -= chart.YAxisRangeChangedNeedRedraw;
                if (e.NewValue != null && e.NewValue is BaseAxis<TY> newaxis) newaxis.RangeChangedEvent += chart.YAxisRangeChangedNeedRedraw;
                chart.Draw();
            }
        }));
        protected virtual void YAxisRangeChangedNeedRedraw(BaseAxis<TY> axis) { Draw(); }

        protected static void PropertyChangedNeedRedraw(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is BaseXYSeries<TX, TY> chart)
            {
                chart.Draw();
            }
        }

        #region 矢量图创建控制
        /// <summary>
        /// 创建矢量数据
        /// </summary>
        /// <returns></returns>
        protected abstract Geometry CreateGeometry(int count, Func<int, double> getXFunc, Func<int, double> getYFunc);

        CancellationTokenSource TokenSource;
        /// <summary>
        /// 绘制图形
        /// </summary>
        /// <param name="ds">更新的数据源集合</param>
        public virtual async void Draw(params XYSeriesDataSource<TX, TY>[] ds)
        {
            if (TokenSource != null && !TokenSource.IsCancellationRequested)
            {
                TokenSource.Cancel();
                TokenSource.Dispose();
            }
            TokenSource = new CancellationTokenSource();
            if (XAxis != null && YAxis != null && DataSource != null && DataSource.Count > 0)
            {
                try
                {
                    var token = TokenSource.Token;
                    var xParam = XAxis.GetValueLocationParam();
                    var yParam = YAxis.GetValueLocationParam();
                    Func<TX, AxisValueLocationConvertParam, double> xValueLocationFunc = XAxis.GetValueLocation;
                    Func<TY, AxisValueLocationConvertParam, double> yValueLocationFunc = YAxis.GetValueLocation;
                    XYSeriesDataSource<TX, TY>[] allds;
                    if (ds == null || ds.Length <= 0) allds = ds = DataSource.ToArray();
                    else allds = DataSource.ToArray();
                    var geometry = await Task.Factory.StartNew((t) =>
                    {
                        if (t is CancellationToken tk)
                        {
                            var geometryGroup = new GeometryGroup() { FillRule = FillRule.Nonzero };
                            foreach (var item in allds)
                            {
                                if (tk.IsCancellationRequested) return null;
                                if (ds.Contains(item))//需要更新图形的数据源
                                {
                                    var getXFunc = new Func<int, double>((i) => xValueLocationFunc(item.GetXFunc(i), xParam));
                                    var getYFunc = new Func<int, double>((i) => yValueLocationFunc(item.GetYFunc(i), yParam));
                                    item.Geometry = CreateGeometry(item.Count, getXFunc, getYFunc);
                                    if (item.Geometry != null) item.Geometry.Freeze();
                                }
                                if (item.Geometry != null) geometryGroup.Children.Add(item.Geometry);
                            }
                            geometryGroup.Freeze();
                            if (tk.IsCancellationRequested) return null;
                            return geometryGroup;
                        }
                        else return null;
                    }, token, token);
                    if (!token.IsCancellationRequested) Geometry = geometry;
                }
                catch (TaskCanceledException e)
                {
                    Debug.WriteLine(string.Concat("创建矢量图线程取消", e.Message));
                }
            }
            else Geometry = null;
        }

        public virtual async void Reshow()
        {
            Geometry = await Task.Run(() =>
            {
                var geometryGroup = new GeometryGroup();
                if (DataSource != null && DataSource.Count > 0)
                {
                    foreach (var item in DataSource)
                    {
                        if (item.Geometry != null) geometryGroup.Children.Add(item.Geometry);
                    }
                }
                geometryGroup.Freeze();
                return geometryGroup;
            });
        }
        #endregion

        #region 数据
        /// <summary>
        /// 数据源
        /// </summary>
        public ObservableCollection<XYSeriesDataSource<TX, TY>> DataSource
        {
            get { return (ObservableCollection<XYSeriesDataSource<TX, TY>>)GetValue(DataSourceProperty); }
            set { SetValue(DataSourceProperty, value); }
        }
        public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", typeof(ObservableCollection<XYSeriesDataSource<TX, TY>>), typeof(BaseXYSeries<TX, TY>), new PropertyMetadata((d, e) =>
        {
            if (d is BaseXYSeries<TX, TY> series)
            {
                if (e.OldValue != null && e.OldValue is ObservableCollection<XYSeriesDataSource<TX, TY>> olddata)
                {
                    foreach (var data in olddata) series.UnregistDataSource(data);
                    olddata.CollectionChanged -= series.DataSource_CollectionChanged;
                }
                if (e.NewValue != null && e.NewValue is ObservableCollection<XYSeriesDataSource<TX, TY>> newdata)
                {
                    foreach (var data in newdata) series.RegistDataSource(data);
                    newdata.CollectionChanged += series.DataSource_CollectionChanged;
                }
                series.Draw();
            }
        }));
        protected virtual void DataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var item in e.NewItems)
                    {
                        if (item is XYSeriesDataSource<TX, TY> data)
                        {
                            RegistDataSource(data);
                            Draw(data);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var item in e.OldItems)
                    {
                        if (item is XYSeriesDataSource<TX, TY> data) UnregistDataSource(data);
                    }
                    Reshow();
                    break;
                case NotifyCollectionChangedAction.Replace:
                    foreach (var item in e.OldItems)
                    {
                        if (item is XYSeriesDataSource<TX, TY> data) UnregistDataSource(data);
                    }
                    foreach (var item in e.NewItems)
                    {
                        if (item is XYSeriesDataSource<TX, TY> data)
                        {
                            RegistDataSource(data);
                            Draw(data);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 数据源属性发生变化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void DataSource_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender is XYSeriesDataSource<TX, TY> data && (nameof(XYSeriesDataSource<TX, TY>.Count).Equals(e.PropertyName) || nameof(XYSeriesDataSource<TX, TY>.GetXFunc).Equals(e.PropertyName) || nameof(XYSeriesDataSource<TX, TY>.GetYFunc).Equals(e.PropertyName)))
            {
                Draw(data);
            }
        }

        protected List<XYSeriesDataSource<TX, TY>> RegistedDataSource = new List<XYSeriesDataSource<TX, TY>>();
        protected virtual void RegistDataSource(params XYSeriesDataSource<TX, TY>[] ds)
        {
            foreach (var data in ds)
            {
                if (!RegistedDataSource.Contains(data))
                {
                    data.PropertyChanged += DataSource_PropertyChanged;
                    RegistedDataSource.Add(data);
                }
            }
        }
        protected virtual void UnregistDataSource(params XYSeriesDataSource<TX, TY>[] ds)
        {
            foreach (var data in ds)
            {
                if (RegistedDataSource.Contains(data))
                {
                    data.PropertyChanged -= DataSource_PropertyChanged;
                    RegistedDataSource.Remove(data);
                }
            }
        }
        #endregion
    }

    public class XYSeriesDataSource<TX, TY> : BindableBase
    {
        private object tag;
        /// <summary>
        /// 标签
        /// </summary>
        public object Tag
        {
            get { return tag; }
            set { SetProperty(ref tag, value); }
        }

        private int count;
        /// <summary>
        /// 数据量
        /// </summary>
        public int Count
        {
            get { return count; }
            set { SetProperty(ref count, value); }
        }

        private Func<int, TX> getXFunc;
        /// <summary>
        /// 获取X值,需要支持多线程
        /// </summary>
        public Func<int, TX> GetXFunc
        {
            get { return getXFunc; }
            set { SetProperty(ref getXFunc, value); }
        }

        private Func<int, TY> getYFunc;
        /// <summary>
        /// 获取Y值,需要支持多线程
        /// </summary>
        public Func<int, TY> GetYFunc
        {
            get { return getYFunc; }
            set { SetProperty(ref getYFunc, value); }
        }

        internal Geometry Geometry { get; set; }
    }
}
